import asyncio
from asyncio.exceptions import TimeoutError


async def play_music(name: str):
    print(f"Start playing {name}")
    await asyncio.sleep(3)
    print(f"Finished playing {name}")

    return name


async def cal_api():
    print("cal_api...")
    raise Exception("cal_api error")


async def my_cancel():
    task = asyncio.create_task(play_music("A"))

    await asyncio.sleep(3)

    # 如果任务未完成，取消任务
    if not task.done():
        task.cancel()


async def my_cancel_with_timeout():
    task = asyncio.create_task(play_music("A"))

    try:
        await asyncio.wait_for(task, timeout=3)
    except TimeoutError:
        print("timeout")  # 超时了，任务被取消


async def my_timeout():
    task = asyncio.create_task(play_music("A"))

    try:
        # asyncio.shield(task) 加上盾牌，避免被取消
        await asyncio.wait_for(asyncio.shield(task), timeout=1)
    except TimeoutError:
        print("timeout")  # 超时了，任务还在
        await task  # 继续等待完成


async def my_gather():
    # 等待多个协程任务完毕
    results = await asyncio.gather(play_music("A"), play_music("B"))
    print(results)  # ['A', 'B']


async def my_gather_with_exception():
    # 有异常，不希望直接崩溃，可以得到结果
    results = await asyncio.gather(play_music("A"), play_music("B"), cal_api(), return_exceptions=True)
    print(results)  # ['A', 'B', Exception('cal_api error')]


if __name__ == '__main__':
    # asyncio.run(my_cancel())

    # asyncio.run(my_cancel_with_timeout())  # 报错

    # asyncio.run(my_timeout())

    # asyncio.run(my_gather())

    asyncio.run(my_gather_with_exception())


